package com.meviewer.service;

import android.net.Uri;
import android.util.Log;
import android.webkit.WebResourceResponse;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ProcessingHPYService {

    public String queryExtent(String[] hpyPathList)
    {
        Double minx = 180.0;
        Double miny = 90.0;
        Double maxx = -180.0;
        Double maxy = -90.0;
        String imageType = "";
        for(int i = 0; i < hpyPathList.length; ++i)
        {
            Uri uri = Uri.parse(hpyPathList[i]);
            String filePath = uri.getPath() + "/infos.txt";
            try
            {
                BufferedReader reader = new BufferedReader(new FileReader(new File(filePath)));
                minx = Math.min(Double.parseDouble(reader.readLine()), minx);
                miny = Math.min(Double.parseDouble(reader.readLine()), miny);
                maxx = Math.max(Double.parseDouble(reader.readLine()), maxx);
                maxy = Math.max(Double.parseDouble(reader.readLine()), maxy);
                imageType = reader.readLine();
            }
            catch(IOException exception)
            {
                exception.printStackTrace();
            }
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("minx",minx);
        jsonObject.put("miny",miny);
        jsonObject.put("maxx",maxx);
        jsonObject.put("maxy",maxy);
        jsonObject.put("imageType", imageType);
        return jsonObject.toJSONString();
    }

    public WebResourceResponse fetchImageFromList(String[] hpyPathList, int x, int y, int z, String imageType)
    {
        for(int i = hpyPathList.length - 1; i >= 0; --i)
        {
            WebResourceResponse response = fetchImage(hpyPathList[i],x, y, z, imageType);
            if (null != response)
            {
                return response;
            }
        }
        return null;
    }

    public WebResourceResponse fetchDEMFromList(String[] hpyPathList, int x, int y, int z)
    {
        for(int i = hpyPathList.length - 1; i >= 0; --i)
        {
            WebResourceResponse response = fetchDEM(hpyPathList[i],x, y, z);
            if (null != response)
            {
                return response;
            }
        }
        return null;
    }

    public WebResourceResponse fetchImage(String hpyPath, int x, int y, int z, String imageType)
    {
        int indexOffset = 0;
        String fileName = "";
        if (z <= 9)
        {
            int sideSize = (int)Math.pow(2, z);
            indexOffset = (y * sideSize + x) * 16;
            fileName = hpyPath + "/blocks_" + z + ".hpy";
        }
        else
        {
            indexOffset = (y % 512 * 512 + x % 512) * 16;
            fileName = hpyPath + "/blocks_" + z + "_" + x / 512 + "_" + y / 512 + ".hpy";
        }

        try (RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "r"))
        {
            randomAccessFile.seek(indexOffset);
            long offset = randomAccessFile.readLong();
            long length = randomAccessFile.readLong();
            if ((offset != -1) && (length != -1))
            {
                randomAccessFile.seek(offset);
                byte[] imageData = new byte[(int)length];
                randomAccessFile.read(imageData);
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageData);
                randomAccessFile.close();
                return new WebResourceResponse(imageType, "utf-8", inputStream);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public WebResourceResponse fetchDEM(String hpyPath, int x, int y, int z)
    {
        int indexOffset = 0;
        String fileName = "";
        if (z <= 9)
        {
            int sideSize = (int)Math.pow(2, z);
            indexOffset = (y * sideSize * 2 + x) * 16;
            fileName = hpyPath + "/blocks_" + z + ".hpy";
        }
        else
        {
            indexOffset = (y % 512 * 512 + x % 512) * 16;
            fileName = hpyPath + "/blocks_" + z + "_" + x / 512 + "_" + y / 512 + ".hpy";
        }

        try (RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "r"))
        {
            randomAccessFile.seek(indexOffset);
            long offset = randomAccessFile.readLong();
            long length = randomAccessFile.readLong();
            if ((offset != -1) && (length != -1))
            {
                randomAccessFile.seek(offset);
                byte[] demData = new byte[(int)length];
                randomAccessFile.read(demData);
                ByteArrayInputStream inputStream = new ByteArrayInputStream(demData);
                randomAccessFile.close();
                return new WebResourceResponse("application/vnd.quantized-mesh", "ASCII", inputStream);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public WebResourceResponse fetchLayerJsonFromList(String[] hpyPathList)
    {
        HashMap<Integer, List<Integer[]>> available = new HashMap<>();
        int maxZ = -1;
        int minZ = 100;

        String projectionStr = null;

        double minX = Double.POSITIVE_INFINITY;
        double minY = Double.POSITIVE_INFINITY;
        double maxX = Double.NEGATIVE_INFINITY;
        double maxY = Double.NEGATIVE_INFINITY;

        for(int i = 0; i < hpyPathList.length; ++i)
        {
            String hpyPath = hpyPathList[i];
            String layerJsonPath = hpyPath + "/layer.json";
            try
            {
                File file = new File(layerJsonPath);
                BufferedReader reader = new BufferedReader(new FileReader(file));

                String jsonContent = "";
                String lineText;
                while((lineText = reader.readLine()) != null) {
                    jsonContent += lineText;
                }

                JSONObject rootObject = JSON.parseObject(jsonContent);
                JSONArray availableArray = rootObject.getJSONArray("available");

                int minZoom = rootObject.getInteger("minzoom");
                int maxZoom = rootObject.getInteger("maxzoom");

                minZ = Math.min(minZoom, minZ);
                maxZ = Math.max(maxZoom, maxZ);

                JSONArray boundArray = rootObject.getJSONArray("bounds");
                double minX_ = boundArray.getDoubleValue(0);
                double minY_ = boundArray.getDoubleValue(1);
                double maxX_ = boundArray.getDoubleValue(2);
                double maxY_ = boundArray.getDoubleValue(3);

                minX = Math.min(minX_, minX);
                minY = Math.min(minY_, minY);
                maxX = Math.max(maxX_, maxX);
                maxY = Math.max(maxY_, maxY);

                if( null != projectionStr)
                {
                    projectionStr = rootObject.getString("projection");
                }

                int arraySize = availableArray.size();
                for(int j = 0; j < arraySize; ++j)
                {
                    JSONObject availableAtZObject = availableArray.getJSONObject(j);
                    Integer z = availableAtZObject.getInteger("z");
                    JSONArray blockArray = availableAtZObject.getJSONArray("blocks");

                    List<Integer[]> blocks = available.get(z);
                    if(blocks == null)
                    {
                        blocks = new ArrayList<>();
                        available.put(z, blocks);
                    }
                    for(int k = 0; k < blockArray.size(); ++k)
                    {
                        JSONObject blockObject = blockArray.getJSONObject(k);
                        Integer startX = blockObject.getInteger("startX");
                        Integer startY = blockObject.getInteger("startY");
                        Integer endX = blockObject.getInteger("endX");
                        Integer endY = blockObject.getInteger("endY");

                        Integer[] block = {startX, startY, endX, endY};
                        blocks.add(block);
                    }
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }

        JSONObject rootObject = new JSONObject();
        JSONArray availableArray = new JSONArray();
        rootObject.put("available", availableArray);

        JSONArray nullArray = new JSONArray();
        for(int i = minZ; i < maxZ; ++i)
        {
            List<Integer[]> blocks = available.get(i);
            if(null == blocks)
            {
                availableArray.add(nullArray);
            }
            else
            {
                JSONArray blockArray = new JSONArray();
                availableArray.add(blockArray);

                for (int j = 0; j < blocks.size(); ++j)
                {
                    Integer[] block = blocks.get(j);
                    JSONObject blockObject = new JSONObject();
                    blockObject.put("startX", block[0]);
                    blockObject.put("startY", block[1]);
                    blockObject.put("endX", block[2]);
                    blockObject.put("endY", block[3]);
                    blockArray.add(blockObject);
                }
            }
        }
        rootObject.put("minzoom", minZ);
        rootObject.put("maxzoom", maxZ);
        rootObject.put("scheme", "tms");
        rootObject.put("projection", projectionStr);
        rootObject.put("format", "quantized-mesh-1.0");
        rootObject.put("name", "MEV");
        rootObject.put("version", "1.0.0");
        rootObject.put("tilejson", "1.0");

        JSONArray boundsArray = new JSONArray();
        boundsArray.add(minX);
        boundsArray.add(minY);
        boundsArray.add(maxX);
        boundsArray.add(maxY);

        rootObject.put("bounds", boundsArray);

        JSONArray tileURLFormatArray = new JSONArray();
        tileURLFormatArray.add("{z}/{x}/{y}.terrain");
        rootObject.put("tiles", tileURLFormatArray);

        String jsonString = rootObject.toJSONString();
        return new WebResourceResponse("application/json", "utf-8",new ByteArrayInputStream(jsonString.getBytes()));
    }
}
